import type { ComputedRef } from 'vue'

import type {
  RouteLocationNormalized,
  Router,
  RouteRecordNormalized,
} from 'vue-router'

import type { ITabDefinition } from '@/interface/preferences/ITabDefinition'

import { preferences } from '@vben-core/preferences'

import { acceptHMRUpdate, defineStore } from 'pinia'
import { toRaw } from 'vue'
import { AnyCommonUtilsHelper } from '@/helper/AnyCommonUtilsHelper'
import { anyProgress } from '@/single/AnyProgress'

interface TabbarState {
  /** # 当前打开的标签页列表缓存 */
  cachedTabs: Set<string>
  /** # 拖拽结束的索引 */
  dragEndIndex: number
  /** # 需要排除缓存的标签页 */
  excludeCachedTabs: Set<string>
  /** # 标签右键菜单列表 */
  menuList: string[]
  /** # 是否刷新 */
  renderRouteView?: boolean
  /** # 当前打开的标签页列表 */
  tabs: ITabDefinition[]
  /** # 更新时间，用于一些更新场景，使用watch深度监听的话，会损耗性能 */
  updateTime?: number
}

/** # 访问权限相关 */
export const useTabbarStore = defineStore('core-tabbar', {
  actions: {
    /** # 批量关闭标签页 */
    async _bulkCloseByKeys(keys: string[]) {
      const keySet = new Set(keys)
      this.tabs = this.tabs.filter(
        item => !keySet.has(getTabKeyFromTab(item)),
      )
      await this.updateCacheTabs()
    },
    /** # 关闭标签页 */
    _close(tab: ITabDefinition) {
      if (isAffixTab(tab)) {
        return
      }
      const index = this.tabs.findIndex(item => equalTab(item, tab))
      index !== -1 && this.tabs.splice(index, 1)
    },
    /**
     * @zh_CN 跳转到默认标签页
     */
    async _goToDefaultTab(router: Router) {
      if (this.getTabs.length <= 0) {
        return
      }
      const firstTab = this.getTabs[0]
      if (firstTab) {
        await this._goToTab(firstTab, router)
      }
    },
    /**
     * @zh_CN 跳转到标签页
     * @param tab
     * @param router
     */
    async _goToTab(tab: ITabDefinition, router: Router) {
      const { params, path, query } = tab
      const toParams = {
        params: params || {},
        path,
        query: query || {},
      }
      await router.replace(toParams)
    },
    /**
     * @zh_CN 添加标签页
     * @param routeTab
     */
    addTab(routeTab: ITabDefinition): ITabDefinition {
      let tab = cloneTab(routeTab)
      if (!tab.key) {
        tab.key = getTabKey(routeTab)
      }
      if (!isTabShown(tab)) {
        return tab
      }

      const tabIndex = this.tabs.findIndex((item) => {
        return equalTab(item, tab)
      })

      if (tabIndex === -1) {
        const maxCount = preferences.tabbar.maxCount
        // 获取动态路由打开数，超过 0 即代表需要控制打开数
        const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab
          ?? -1) as number
        // 如果动态路由层级大于 0 了，那么就要限制该路由的打开数限制了
        // 获取到已经打开的动态路由数, 判断是否大于某一个值
        if (
          maxNumOfOpenTab > 0
          && this.tabs.filter(tab => tab.name === routeTab.name).length
          >= maxNumOfOpenTab
        ) {
          // 关闭第一个
          const index = this.tabs.findIndex(
            item => item.name === routeTab.name,
          )
          index !== -1 && this.tabs.splice(index, 1)
        }
        else if (maxCount > 0 && this.tabs.length >= maxCount) {
          // 关闭第一个
          const index = this.tabs.findIndex(
            item =>
              !Reflect.has(item.meta, 'affixTab') || !item.meta.affixTab,
          )
          index !== -1 && this.tabs.splice(index, 1)
        }
        this.tabs.push(tab)
      }
      else {
        // 页面已经存在，不重复添加选项卡，只更新选项卡参数
        const currentTab = toRaw(this.tabs)[tabIndex]
        const mergedTab = {
          ...currentTab,
          ...tab,
          meta: { ...currentTab?.meta, ...tab.meta },
        }
        if (currentTab) {
          const curMeta = currentTab.meta
          if (Reflect.has(curMeta, 'affixTab')) {
            mergedTab.meta.affixTab = curMeta.affixTab
          }
          if (Reflect.has(curMeta, 'newTabTitle')) {
            mergedTab.meta.newTabTitle = curMeta.newTabTitle
          }
        }
        tab = mergedTab
        this.tabs.splice(tabIndex, 1, mergedTab)
      }
      this.updateCacheTabs()
      return tab
    },
    /**
     * @zh_CN 关闭所有标签页
     */
    async closeAllTabs(router: Router) {
      const newTabs = this.tabs.filter(tab => isAffixTab(tab))
      this.tabs = newTabs.length > 0 ? newTabs : [...this.tabs].splice(0, 1)
      await this._goToDefaultTab(router)
      this.updateCacheTabs()
    },
    /**
     * @zh_CN 关闭左侧标签页
     * @param tab
     */
    async closeLeftTabs(tab: ITabDefinition) {
      const index = this.tabs.findIndex(item => equalTab(item, tab))

      if (index < 1) {
        return
      }

      const leftTabs = this.tabs.slice(0, index)
      const keys: string[] = []

      for (const item of leftTabs) {
        if (!isAffixTab(item)) {
          keys.push(item.key as string)
        }
      }
      await this._bulkCloseByKeys(keys)
    },
    /**
     * @zh_CN 关闭其他标签页
     * @param tab
     */
    async closeOtherTabs(tab: ITabDefinition) {
      const closeKeys = this.tabs.map(item => getTabKeyFromTab(item))

      const keys: string[] = []

      for (const key of closeKeys) {
        if (key !== getTabKeyFromTab(tab)) {
          const closeTab = this.tabs.find(
            item => getTabKeyFromTab(item) === key,
          )
          if (!closeTab) {
            continue
          }
          if (!isAffixTab(closeTab)) {
            keys.push(closeTab.key as string)
          }
        }
      }
      await this._bulkCloseByKeys(keys)
    },
    /**
     * @zh_CN 关闭右侧标签页
     * @param tab
     */
    async closeRightTabs(tab: ITabDefinition) {
      const index = this.tabs.findIndex(item => equalTab(item, tab))

      if (index !== -1 && index < this.tabs.length - 1) {
        const rightTabs = this.tabs.slice(index + 1)

        const keys: string[] = []
        for (const item of rightTabs) {
          if (!isAffixTab(item)) {
            keys.push(item.key as string)
          }
        }
        await this._bulkCloseByKeys(keys)
      }
    },

    /**
     * @zh_CN 关闭标签页
     * @param tab
     * @param router
     */
    async closeTab(tab: ITabDefinition, router: Router) {
      const { currentRoute } = router
      // 关闭不是激活选项卡
      if (getTabKey(currentRoute.value) !== getTabKeyFromTab(tab)) {
        this._close(tab)
        this.updateCacheTabs()
        return
      }
      const index = this.getTabs.findIndex(
        item => getTabKeyFromTab(item) === getTabKey(currentRoute.value),
      )

      const before = this.getTabs[index - 1]
      const after = this.getTabs[index + 1]

      // 下一个tab存在，跳转到下一个
      if (after) {
        this._close(tab)
        await this._goToTab(after, router)
        // 上一个tab存在，跳转到上一个
      }
      else if (before) {
        this._close(tab)
        await this._goToTab(before, router)
      }
      else {
        console.error('Failed to close the tab; only one tab remains open.')
      }
    },

    /**
     * @zh_CN 通过key关闭标签页
     * @param key
     * @param router
     */
    async closeTabByKey(key: string, router: Router) {
      const originKey = decodeURIComponent(key)
      const index = this.tabs.findIndex(
        item => getTabKeyFromTab(item) === originKey,
      )
      if (index === -1) {
        return
      }

      const tab = this.tabs[index]
      if (tab) {
        await this.closeTab(tab, router)
      }
    },

    /**
     * 根据tab的key获取tab
     * @param key
     */
    getTabByKey(key: string) {
      return this.getTabs.find(
        item => getTabKeyFromTab(item) === key,
      ) as ITabDefinition
    },
    /**
     * @zh_CN 新窗口打开标签页
     * @param tab
     */
    async openTabInNewWindow(tab: ITabDefinition) {
      AnyCommonUtilsHelper.openRouteInNewWindow(tab.fullPath || tab.path)
    },

    /**
     * @zh_CN 固定标签页
     * @param tab
     */
    async pinTab(tab: ITabDefinition) {
      const index = this.tabs.findIndex(item => equalTab(item, tab))
      if (index === -1) {
        return
      }
      const oldTab = this.tabs[index]
      tab.meta.affixTab = true
      tab.meta.title = oldTab?.meta?.title as string
      // this.addTab(tab);
      this.tabs.splice(index, 1, tab)
      // 过滤固定tabs，后面更改affixTabOrder的值的话可能会有问题，目前行464排序affixTabs没有设置值
      const affixTabs = this.tabs.filter(tab => isAffixTab(tab))
      // 获得固定tabs的index
      const newIndex = affixTabs.findIndex(item => equalTab(item, tab))
      // 交换位置重新排序
      await this.sortTabs(index, newIndex)
    },

    /**
     * 刷新标签页
     */
    async refresh(router: Router | string) {
      // 如果是Router路由，那么就根据当前路由刷新
      // 如果是string字符串，为路由名称，则定向刷新指定标签页，不能是当前路由名称，否则不会刷新
      if (typeof router === 'string') {
        return await this.refreshByName(router)
      }

      const { currentRoute } = router
      const { name } = currentRoute.value

      this.excludeCachedTabs.add(name as string)
      this.renderRouteView = false
      anyProgress.start()

      await new Promise(resolve => setTimeout(resolve, 200))

      this.excludeCachedTabs.delete(name as string)
      this.renderRouteView = true
      anyProgress.done()
    },

    /**
     * 根据路由名称刷新指定标签页
     */
    async refreshByName(name: string) {
      this.excludeCachedTabs.add(name)
      await new Promise(resolve => setTimeout(resolve, 200))
      this.excludeCachedTabs.delete(name)
    },

    /**
     * @zh_CN 重置标签页标题
     */
    async resetTabTitle(tab: ITabDefinition) {
      if (tab?.meta?.newTabTitle) {
        return
      }
      const findTab = this.tabs.find(item => equalTab(item, tab))
      if (findTab) {
        findTab.meta.newTabTitle = undefined
        await this.updateCacheTabs()
      }
    },

    /**
     * 设置固定标签页
     * @param tabs
     */
    setAffixTabs(tabs: RouteRecordNormalized[]) {
      for (const tab of tabs) {
        tab.meta.affixTab = true
        this.addTab(routeToTab(tab))
      }
    },

    /**
     * @zh_CN 更新菜单列表
     * @param list
     */
    setMenuList(list: string[]) {
      this.menuList = list
    },

    /**
     * @zh_CN 设置标签页标题
     *
     * @zh_CN 支持设置静态标题字符串或计算属性作为动态标题
     * @zh_CN 当标题为计算属性时,标题会随计算属性值变化而自动更新
     * @zh_CN 适用于需要根据状态或多语言动态更新标题的场景
     *
     * @param {ITabDefinition} tab - 标签页对象
     * @param {ComputedRef<string> | string} title - 标题内容,支持静态字符串或计算属性
     *
     * @example
     * // 设置静态标题
     * setTabTitle(tab, '新标签页');
     *
     * @example
     * // 设置动态标题
     * setTabTitle(tab, computed(() => t('common.dashboard')));
     */
    async setTabTitle(tab: ITabDefinition, title: ComputedRef<string> | string) {
      const findTab = this.tabs.find(item => equalTab(item, tab))

      if (findTab) {
        findTab.meta.newTabTitle = title

        await this.updateCacheTabs()
      }
    },
    setUpdateTime() {
      this.updateTime = Date.now()
    },
    /**
     * @zh_CN 设置标签页顺序
     * @param oldIndex
     * @param newIndex
     */
    async sortTabs(oldIndex: number, newIndex: number) {
      const currentTab = this.tabs[oldIndex]
      if (!currentTab) {
        return
      }
      this.tabs.splice(oldIndex, 1)
      this.tabs.splice(newIndex, 0, currentTab)
      this.dragEndIndex = this.dragEndIndex + 1
    },

    /**
     * @zh_CN 切换固定标签页
     * @param tab
     */
    async toggleTabPin(tab: ITabDefinition) {
      const affixTab = tab?.meta?.affixTab ?? false

      await (affixTab ? this.unpinTab(tab) : this.pinTab(tab))
    },

    /**
     * @zh_CN 取消固定标签页
     * @param tab
     */
    async unpinTab(tab: ITabDefinition) {
      const index = this.tabs.findIndex(item => equalTab(item, tab))
      if (index === -1) {
        return
      }
      const oldTab = this.tabs[index]
      tab.meta.affixTab = false
      tab.meta.title = oldTab?.meta?.title as string
      // this.addTab(tab);
      this.tabs.splice(index, 1, tab)
      // 过滤固定tabs，后面更改affixTabOrder的值的话可能会有问题，目前行464排序affixTabs没有设置值
      const affixTabs = this.tabs.filter(tab => isAffixTab(tab))
      // 获得固定tabs的index,使用固定tabs的下一个位置也就是活动tabs的第一个位置
      const newIndex = affixTabs.length
      // 交换位置重新排序
      await this.sortTabs(index, newIndex)
    },
    /**
     * 根据当前打开的选项卡更新缓存
     */
    async updateCacheTabs() {
      const cacheMap = new Set<string>()

      for (const tab of this.tabs) {
        // 跳过不需要持久化的标签页
        const keepAlive = tab.meta?.keepAlive
        if (!keepAlive) {
          continue
        }
        (tab.matched || []).forEach((t, i) => {
          if (i > 0) {
            cacheMap.add(t.name as string)
          }
        })

        const name = tab.name as string
        cacheMap.add(name)
      }
      this.cachedTabs = cacheMap
    },
  },
  getters: {
    affixTabs(): ITabDefinition[] {
      const affixTabs = this.tabs.filter(tab => isAffixTab(tab))

      return affixTabs.sort((a, b) => {
        const orderA = (a.meta?.affixTabOrder ?? 0) as number
        const orderB = (b.meta?.affixTabOrder ?? 0) as number
        return orderA - orderB
      })
    },
    getCachedTabs(): string[] {
      return [...this.cachedTabs]
    },
    getExcludeCachedTabs(): string[] {
      return [...this.excludeCachedTabs]
    },
    getMenuList(): string[] {
      return this.menuList
    },
    getTabs(): ITabDefinition[] {
      const normalTabs = this.tabs.filter(tab => !isAffixTab(tab))
      return [...this.affixTabs, ...normalTabs].filter(Boolean)
    },
  },
  persist: [
    // tabs不需要保存在localStorage
    {
      pick: ['tabs'],
      storage: sessionStorage,
    },
  ],
  state: (): TabbarState => ({
    cachedTabs: new Set(),
    dragEndIndex: 0,
    excludeCachedTabs: new Set(),
    menuList: [
      'close',
      'affix',
      'maximize',
      'reload',
      'open-in-new-window',
      'close-left',
      'close-right',
      'close-other',
      'close-all',
    ],
    renderRouteView: true,
    tabs: [],
    updateTime: Date.now(),
  }),
})

// 解决热更新问题
const hot = import.meta.hot
if (hot) {
  hot.accept(acceptHMRUpdate(useTabbarStore, hot))
}

/**
 * @zh_CN 克隆路由,防止路由被修改
 * @param route
 */
function cloneTab(route: ITabDefinition): ITabDefinition {
  if (!route) {
    return route
  }
  const { matched, meta, ...opt } = route
  return {
    ...opt,
    matched: (matched
      ? matched.map(item => ({
          meta: item.meta,
          name: item.name,
          path: item.path,
        }))
      : undefined) as RouteRecordNormalized[],
    meta: {
      ...meta,
      newTabTitle: meta.newTabTitle,
    },
  }
}

/**
 * @zh_CN 是否是固定标签页
 * @param tab
 */
function isAffixTab(tab: ITabDefinition) {
  return tab?.meta?.affixTab ?? false
}

/**
 * @zh_CN 是否显示标签
 * @param tab
 */
function isTabShown(tab: ITabDefinition) {
  const matched = tab?.matched ?? []
  return !tab.meta.hideInTab && matched.every(item => !item.meta.hideInTab)
}

/**
 * 从route获取tab页的key
 * @param tab
 */
function getTabKey(tab: RouteLocationNormalized | RouteRecordNormalized) {
  const {
    fullPath,
    path,
    meta: { fullPathKey } = {},
    query = {},
  } = tab as RouteLocationNormalized
  // pageKey可能是数组（查询参数重复时可能出现）
  const pageKey = Array.isArray(query.pageKey)
    ? query.pageKey[0]
    : query.pageKey
  let rawKey
  if (pageKey) {
    rawKey = pageKey
  }
  else {
    rawKey = fullPathKey === false ? path : (fullPath ?? path)
  }
  try {
    return decodeURIComponent(rawKey)
  }
  catch {
    return rawKey
  }
}

/**
 * 从tab获取tab页的key
 * 如果tab没有key,那么就从route获取key
 * @param tab
 */
function getTabKeyFromTab(tab: ITabDefinition): string {
  return tab.key ?? getTabKey(tab)
}

/**
 * 比较两个tab是否相等
 * @param a
 * @param b
 */
function equalTab(a: ITabDefinition, b: ITabDefinition) {
  return getTabKeyFromTab(a) === getTabKeyFromTab(b)
}

function routeToTab(route: RouteRecordNormalized) {
  return {
    meta: route.meta,
    name: route.name,
    path: route.path,
    key: getTabKey(route),
  } as ITabDefinition
}

export { getTabKey }
